#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
主题: 字符串中插入变量
问题: 你想创建一个内嵌变量的字符串，变量被它的值所表示的字符串替换掉。
提示:
    1. format() 和 format_map() 更加先进，因此应该被优先选择
"""
import sys

s = '{name} has {n} messages'
print(f"{s = }")
def recipe1():
    """format() 方法"""
    
    print(f"{s.format(name = 'Guido', n=37) = }")
    name = 'Guido'                          # NOSONAR
    n = 37                                  # NOSONAR
    print(f"{s.format_map(vars()) = }")

def recipe2():
    """vars() 也适用于对象实例"""
    info = Info("Guido", 37)
    print(f"{s.format_map(vars(info)) = }")

def recipe3():
    """format 和 format_map() 的一个缺陷就是它们并不能很好的处理变量缺失的情况"""
    #s.format(name='Guido') # KeyError
    name = 'Guido'                          # NOSONAR
    print(f"{s.format_map(safesub(vars())) = }")

def recipe4():
    name = 'Guido'                          # NOSONAR
    n = 37                                  # NOSONAR
    print(f"{sub('Hello {name}') = }")
    print(f"{sub('You have {n} messages.') = }")
    print(f"{sub('Your favorite color is {color}') = }")

def recipe5():
    """历史的解决放方案"""
    name = 'Guido'                          # NOSONAR
    n = 37                                  # NOSONAR
    #print('%(name) has %(n) messages.' % vars()) # ValueError: unsupported format character 'm' (0x6d) at index 17

    import string
    # 字符串模板
    t = string.Template('$name has $n messages.')
    print(f"{t = }")
    print(f"{t.substitute(vars()) = }")

def sub(text):
    """将变量替换步骤用一个工具函数封装起来"""
    return text.format_map(safesub(sys._getframe(1).f_locals))

class Info:
    def __init__(self, name, n) -> None:
        self.name = name
        self.n = n

class safesub(dict):
    """防止key找不到"""
    def __missing__(self, key):
        return '{' + key + '}'


def main():
    print('recipe1'.center(20, '*'))
    recipe1()
    print('recipe2'.center(20, '*'))
    recipe2()
    print('recipe3'.center(20, '*'))
    recipe3()
    print('recipe4'.center(20, '*'))
    recipe4()
    print('recipe5'.center(20, '*'))
    recipe5()    

if __name__ == '__main__':
    main()          